/*
 * binsem.c
 *
 *  Created on: Apr 9, 2011
 *      Author: Nir&Arseny
 */

#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "x86.h"
#include "proc.h"
#include "spinlock.h"
#include "binsem.h"

int nextBinSemId = 0;

//which returns an identifier (binary_sem_ID) of a newly created and initialized
//semaphore.
int binary_sem_create(){
	// number of possible semaphores is limited to BINSEMCOUNT.
	if (nextBinSemId >= BINSEMCOUNT)
		panic("Semaphore table overflow");

	acquire(&semtable.lock);
	initlock(&semtable.binsems[nextBinSemId].lock, (char*)0);
	semtable.binsems[nextBinSemId].enabled = 1; // not in use
	semtable.binsems[nextBinSemId].counter = 1; // sem value

	// In order to facilitate a queue behavior we keep an index into ptable
	// which will be incremented every time someone issues binary_sem_up command
	semtable.binsems[nextBinSemId].nextIndexToWake = 0;
	nextBinSemId++;
	release(&semtable.lock);
	return (nextBinSemId-1); //return the correct ID
}

//Returns 0 if successful, -1 if an error occurred.
int binary_sem_down(int binary_sem_ID){
	struct binsem * sem;

	// Is this sem id legal? was it created?
	if (binary_sem_ID >= nextBinSemId)
		return -1;
	if (!(semtable.binsems[binary_sem_ID].enabled))
		return -2;

	// will our channel for sleeping (hopefully this pointer is unique :) )
	sem = &semtable.binsems[binary_sem_ID];
	acquire(&(sem->lock));
	// even though wakeupSem will wake up a single thread
	// we take precaution and check that you actually can
	// decrement the sem value
	while(sem->counter<=0){
		//sleep frees sem->lock & re-acquire it after waking up.
		sleep((void *)&semtable.binsems[binary_sem_ID], &sem->lock);
	}
	sem->counter = 0;
	release(&sem->lock);

	return 0;
}


//Upon success, 0 is returned. In case of error, -1 is returned
int binary_sem_up(int binary_sem_ID){
	struct binsem * sem;


	if (binary_sem_ID >= nextBinSemId)
		return -1;
	sem = &semtable.binsems[binary_sem_ID];
	acquire(&sem->lock);
	sem->counter = 1;
	// Wake up a SINGLE thread which sleeps on this semaphore
	wakeupSem((void *)&semtable.binsems[binary_sem_ID], sem->nextIndexToWake++);
	release(&sem->lock);

	return 0;
}
